﻿/********
* 
* 
*  Description:
*  Create By kain.hong at 8/10/2010 11:10:45 AM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/
#region namespace

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using Cherry.Agile.Security;
using Cherry.Agile.ServiceModel.Security;
using System.Threading;
using Microsoft.Practices.Unity;
using System.Diagnostics;
using Cherry.Agile.ServiceModel.Runtime.Instrumentation;
using System.Runtime.Remoting.Messaging;
using System.Security;
#endregion


namespace Cherry.Agile.ServiceModel.Interceptor
{
    internal class ContextReceivalCallContextInitializer : ICallContextInitializer
    {
        public bool IsBidirectional
        { get; set; }

        public PolicyLevel PolicyLevel { get; set; }
        

        //public ContextReceivalCallContextInitializer()
        //    : this(false)
        //{ }

        public ContextReceivalCallContextInitializer(bool isBidirectional, PolicyLevel policyLevel)
        {
            this.IsBidirectional = isBidirectional;
            this.PolicyLevel = policyLevel;
        }
        
        public void AfterInvoke(object correlationState)
        {
            try
            {
                ContextLifetimeManager.Clear();
                var duration = CallContext.GetData(Key);
                if (duration != null)
                {
                    var time = Environment.TickCount - (int)duration;
                    Instrumentation.InvokeDuration(time);
                }
            }
            catch
            {
            }
          
            if (!this.IsBidirectional)
                return;

            ApplicationContext context = correlationState as ApplicationContext;
            if (context == null)
                return;

            Trace.TraceInformation("ContextReceivalCallContextInitializer\tput application context data into message header");

            MessageHeader<ApplicationContext> contextHeader = new MessageHeader<ApplicationContext>(context);
            OperationContext.Current.OutgoingMessageHeaders.Add(contextHeader.GetUntypedHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace));
            ApplicationContext.Current = null;
             
        }

        int _duration;
        const string Key = "_duration";

        public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
        {
            
            _duration = Environment.TickCount;
            CallContext.SetData(Key, _duration);
            if (IsBidirectional)
            {
                var index = message.Headers.FindHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace);
                if (index < 0)
                    return null;
                ApplicationContext context = message.Headers.GetHeader<ApplicationContext>(index);// ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace);
                if (context == null)
                {
                    Trace.TraceWarning("ContextReceivalCallContextInitializer\t" + channel.RemoteAddress + ": token is null");
                    return null;
                }
                
                ApplicationContext.Current = context;
                var token = TokenProvider.Current.GetCurrentToken();
                if (!string.IsNullOrEmpty(context.CultureName))
                    System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(context.CultureName);

                if (token != null)
                {
                    Thread.CurrentPrincipal = Authorization.CreatePrincipal(token.Value.Identity);
                    Trace.TraceInformation("ContextReceivalCallContextInitializer\t" + channel.RemoteAddress + ": create principal");
                }
                else
                {
                    Trace.TraceWarning("ContextReceivalCallContextInitializer\t" + channel.RemoteAddress + ": token is null");
                }
                return ApplicationContext.Current;
            }

            return null;
        }

 
    }
}
